/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

///////////////////////////////////////////////////////////////////
#define LOG_NDEBUG 0 //开启调试ALOGV才会有输出

#define LOG_TAG "MyTest" //开始需要先定义LOG_TAG，否则会使用系统默认的。

#define PRODUCER_CLIENT_NAME "MyProducer"
#define EXTRA_TAG PRODUCER_CLIENT_NAME
///////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <binder/IServiceManager.h>

#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
#include <gui/IProducerListener.h>
#include <ui/GraphicBuffer.h>
#include <gui/Surface.h>

#include <system/window.h>

///////////////////////////////////////////////////////////////////////////////

#define LOGV(x, ...) ALOGV("[%s] " x, EXTRA_TAG, ##__VA_ARGS__)
#define LOGD(x, ...) ALOGD("[%s] " x, EXTRA_TAG, ##__VA_ARGS__)
#define LOGI(x, ...) ALOGI("[%s] " x, EXTRA_TAG, ##__VA_ARGS__)
#define LOGW(x, ...) ALOGW("[%s] " x, EXTRA_TAG, ##__VA_ARGS__)
#define LOGE(x, ...) ALOGE("[%s] " x, EXTRA_TAG, ##__VA_ARGS__)

#define LOGV_IF(cond, x, ...) ALOGV_IF(cond, "[%s] " x, EXTRA_TAG, ##__VA_ARGS__)
#define LOGD_IF(cond, x, ...) ALOGD_IF(cond, "[%s] " x, EXTRA_TAG, ##__VA_ARGS__)
#define LOGI_IF(cond, x, ...) ALOGI_IF(cond, "[%s] " x, EXTRA_TAG, ##__VA_ARGS__)
#define LOGW_IF(cond, x, ...) ALOGW_IF(cond, "[%s] " x, EXTRA_TAG, ##__VA_ARGS__)
#define LOGE_IF(cond, x, ...) ALOGE_IF(cond, "[%s] " x, EXTRA_TAG, ##__VA_ARGS__)

////////////////////////////////////////////////////////////////////////////////

using namespace android;

#if 0
int main(int argc, char** argv)
{
    sp<IServiceManager> serviceManager = defaultServiceManager();
    sp<IBinder> binderProducer = serviceManager->getService(String16("MyProducer"));
    sp<IBinder> binderConsumer = serviceManager->getService(String16("MyConsumer"));
    sp<IGraphicBufferProducer> producer = interface_cast<IGraphicBufferProducer>(binderProducer);
    sp<IGraphicBufferConsumer> consumer = interface_cast<IGraphicBufferConsumer>(binderConsumer);

    IGraphicBufferProducer::QueueBufferOutput output;
    producer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output);

    /****************** Producer ***********************/
    int slot;
    sp<Fence> fence;
    sp<GraphicBuffer> buffer;
    producer->dequeueBuffer(&slot, &fence, 0, 0, 0,
		    	     GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr);
    producer->requestBuffer(slot, &buffer);

    uint32_t* dataIn;
    buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
		 reinterpret_cast<void**>(&dataIn));
    *dataIn = 0x12345678u;
    buffer->unlock();

    IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(1, 1),
				    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
    producer->queueBuffer(slot, input, &output);


    /****************** Consumer ***********************/
    BufferItem item;
    consumer->acquireBuffer(&item, static_cast<nsecs_t>(0));

    uint32_t* dataOut;
    item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
		    	      reinterpret_cast<void**>(&dataOut));
    LOGD("the data out is 0x%08x\n", *dataOut);
    item.mGraphicBuffer->unlock();

    consumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY,
		    	     EGL_NO_SYNC_KHR, Fence::NO_FENCE);

    return 0;
}

#else

int32_t g_quit = 0;

static void signal_handler_set_quit(int sig)
{
    LOGI("received signal %d\n", sig);
    g_quit = 1;
}

int main_22(int argc, char** argv)
{
    LOGI("client running...\n");

    sp<IServiceManager> serviceManager = defaultServiceManager();
    sp<IBinder> binderProducer = serviceManager->getService(String16("MyProducer"));
    sp<IGraphicBufferProducer> producer = interface_cast<IGraphicBufferProducer>(binderProducer);

    IGraphicBufferProducer::QueueBufferOutput output;
    producer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output);

    //producer->setAsyncMode(false); //是否设置都没什么效果？
    //producer->setAsyncMode(true);

    status_t err = NO_ERROR;
    int minUndequeuedBuffers;
    err = producer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);

    LOGI("minUndequeuedBuffers = %d\n", minUndequeuedBuffers);

    int maxDequeuedBuffers = 1;
    producer->setMaxDequeuedBufferCount(maxDequeuedBuffers);

#define AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 0x23
    int format = AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420;
    int width = 1920;
    int height = 1080;

    uint32_t val = 0;

    while (!g_quit)
    {
        int slot;
        sp<Fence> fence;
        sp<GraphicBuffer> buffer;
        producer->dequeueBuffer(&slot, &fence, width, height, format, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr);
        producer->requestBuffer(slot, &buffer);

        uint32_t* dataIn;
        buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&dataIn));
        *dataIn = val;
        buffer->unlock();

        IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(1, 1),
                        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
        producer->queueBuffer(slot, input, &output);

        LOGD("queueBuffer slot=%d, val=%d, dataIn=0x%x\n", slot, val, dataIn);

        //sleep
        //usleep(40000);

        val++;
    }

    LOGI("client exited.\n");

    return 0;
}



static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();

class DummySurfaceListener : public SurfaceListener {
public:
    DummySurfaceListener(bool enableReleasedCb = false) :
            mEnableReleaseCb(enableReleasedCb),
            mBuffersReleased(0) {}
    virtual ~DummySurfaceListener() = default;

    virtual void onBufferReleased() {
        mBuffersReleased++;
    }
    virtual bool needsReleaseNotify() {
        return mEnableReleaseCb;
    }
    virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) {
        mDiscardedBuffers.insert(mDiscardedBuffers.end(), buffers.begin(), buffers.end());
    }

    int getReleaseNotifyCount() const {
        return mBuffersReleased;
    }
    const std::vector<sp<GraphicBuffer>>& getDiscardedBuffers() const {
        return mDiscardedBuffers;
    }
private:
    // No need to use lock given the test triggers the listener in the same
    // thread context.
    bool mEnableReleaseCb;
    int32_t mBuffersReleased;
    std::vector<sp<GraphicBuffer>> mDiscardedBuffers;
};


int main_bck(int argc, char** argv)
{
    LOGI("22 client running...\n");

    sp<IServiceManager> serviceManager = defaultServiceManager();
    sp<IBinder> binderProducer = serviceManager->getService(String16("MyProducer"));
    if (binderProducer == nullptr)
    {
        LOGE("binderProducer is null");
    }
    else
    {
        LOGE("binderProducer is ok");
    }

    sp<IGraphicBufferProducer> producer = interface_cast<IGraphicBufferProducer>(binderProducer);
    if (producer == nullptr)
    {
        LOGE("producer is null");
    }
    else
    {
        LOGE("producer is ok");
    }

    sp<Surface> surface = new Surface(producer);
    sp<ANativeWindow> window(surface);
    sp<DummySurfaceListener> listener;
    listener = new DummySurfaceListener(false);

    //    IGraphicBufferProducer::QueueBufferOutput output;
    //producer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output);

    surface->connect(NATIVE_WINDOW_API_CPU, /*reportBufferRemoval*/true, /*listener*/listener);

    //producer->setAsyncMode(false); //是否设置都没什么效果？
    //producer->setAsyncMode(true);

    status_t err = NO_ERROR;
    int minUndequeuedBuffers;
    //err = producer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);

    //LOGI("minUndequeuedBuffers = %d\n", minUndequeuedBuffers);

    //int maxDequeuedBuffers = 1;
    //producer->setMaxDequeuedBufferCount(maxDequeuedBuffers);

#define AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 0x23
    int format = AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420;
    int width = 1920;
    int height = 1080;

    uint32_t frame_size = width * height * 1.5;

    err = ANativeWindow_setBuffersGeometry(window.get(), width, height, format);
    if (err != NO_ERROR)
    {
        LOGE("ANativeWindow_setBuffersGeometry failed\n");
    }

    //ANativeWindow_setBufferCount(window.get(), BUFFER_COUNT);

#define USE_SET_BUFFER_COUNT 0

    //native_window_connect
#if USE_SET_BUFFER_COUNT //跟系统一起用时，fakecamera serivice已经设置了
    const int BUFFER_COUNT = 4;
    err = native_window_set_buffer_count(window.get(), BUFFER_COUNT);
    if (err != NO_ERROR)
    {
    }

    ANativeWindowBuffer* buffers[BUFFER_COUNT];
    // Dequeue first to allocate a number of buffers
    for (int i = 0; i < BUFFER_COUNT -1; i++) {
        err = native_window_dequeue_buffer_and_wait(window.get(), &buffers[i]);
        if (err != NO_ERROR)
        {
            LOGE("native_window_dequeue_buffer_and_wait failed, err=%d\n", err);
        }
        else
        {
            LOGD("native_window_dequeue_buffer_and_wait ok.\n");
        }
    }

    LOGD("###11###############################\n");

    for (int i = 0; i < BUFFER_COUNT-1; i++) {
        window->cancelBuffer(window.get(), buffers[i], -1);
    }
#endif

    LOGD("###22###############################\n");

    ANativeWindowBuffer* buffer;
    // Fill BUFFER_COUNT-1 buffers
    // for (int i = 0; i < BUFFER_COUNT-1; i++) {
    //     native_window_dequeue_buffer_and_wait(window.get(), &buffer);
    //     window->queueBuffer(window.get(), buffer, -1);
    // }

    uint32_t val = 0;
    ANativeWindowBuffer* pnwb;
    GraphicBuffer *gpb;

    FILE *fp = fopen("/data/local/tmp/1080p_nv12.yuv", "rb");
    if (fp == NULL)
    {
        LOGE("fopen failed!\n");
    }

    while (!g_quit)
    {

        err = native_window_dequeue_buffer_and_wait(window.get(), &pnwb);
        if (err != NO_ERROR)
        {
            LOGE("native_window_dequeue_buffer_and_wait failed, err=%d\n", err);
        }
        else
        {
            LOGD("111 native_window_dequeue_buffer_and_wait ok.\n");

            gpb = static_cast<GraphicBuffer *>(pnwb);

            uint32_t* dataIn;
            gpb->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&dataIn));
            //*dataIn = val;

            int32_t read_size = fread(dataIn, 1, frame_size, fp);
            if (read_size != frame_size)
            {
                rewind(fp);
                read_size = fread(dataIn, 1, frame_size, fp);
                if (read_size != frame_size)
                {
                    LOGE("read error!\n");
                }
            }

            gpb->unlock();

            LOGD("111 native_window_dequeue_buffer_and_wait ok. val=%d \n", val);

            window->queueBuffer(window.get(), pnwb, -1);

            val++;
        }

        //sleep
        usleep(400000);
    }

    if (fp)
        fclose(fp);

    // Disconnect the surface
    surface->disconnect(NATIVE_WINDOW_API_CPU);

    LOGI("client exited.\n");

    return 0;
}

//void *bq_producer_client_thread(void *arg)
void bq_producer_client_thread()
{
    LOGI("22 client running...\n");

    sp<IServiceManager> serviceManager = defaultServiceManager();
    sp<IBinder> binderProducer = serviceManager->getService(String16("MyProducer"));
    if (binderProducer == nullptr)
    {
        LOGE("binderProducer is null");
    }
    else
    {
        LOGE("binderProducer is ok");
    }

    sp<IGraphicBufferProducer> producer = interface_cast<IGraphicBufferProducer>(binderProducer);
    if (producer == nullptr)
    {
        LOGE("producer is null");
    }
    else
    {
        LOGE("producer is ok");
    }

    sp<Surface> surface = new Surface(producer);
    sp<ANativeWindow> window(surface);
    sp<DummySurfaceListener> listener;
    listener = new DummySurfaceListener(false);

    //    IGraphicBufferProducer::QueueBufferOutput output;
    //producer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output);

    surface->connect(NATIVE_WINDOW_API_CPU, /*reportBufferRemoval*/true, /*listener*/listener);

    //producer->setAsyncMode(false); //是否设置都没什么效果？
    //producer->setAsyncMode(true);

    status_t err = NO_ERROR;
    int minUndequeuedBuffers;
    //err = producer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);

    //LOGI("minUndequeuedBuffers = %d\n", minUndequeuedBuffers);

    //int maxDequeuedBuffers = 1;
    //producer->setMaxDequeuedBufferCount(maxDequeuedBuffers);

#define AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 0x23
    int format = AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420;
    int width = 1920;
    int height = 1080;

    uint32_t frame_size = width * height * 1.5;

    err = ANativeWindow_setBuffersGeometry(window.get(), width, height, format);
    if (err != NO_ERROR)
    {
        LOGE("ANativeWindow_setBuffersGeometry failed\n");
    }

    //ANativeWindow_setBufferCount(window.get(), BUFFER_COUNT);

#define USE_SET_BUFFER_COUNT 1

    //native_window_connect
#if USE_SET_BUFFER_COUNT //跟系统一起用时，fakecamera serivice已经设置了
    const int BUFFER_COUNT = 4;
    err = native_window_set_buffer_count(window.get(), BUFFER_COUNT);
    if (err != NO_ERROR)
    {
    }

    ANativeWindowBuffer* buffers[BUFFER_COUNT];
    // Dequeue first to allocate a number of buffers
    for (int i = 0; i < BUFFER_COUNT -1; i++) {
        err = native_window_dequeue_buffer_and_wait(window.get(), &buffers[i]);
        if (err != NO_ERROR)
        {
            LOGE("native_window_dequeue_buffer_and_wait failed, err=%d\n", err);
        }
        else
        {
            LOGD("native_window_dequeue_buffer_and_wait ok.\n");


        }
    }

    LOGD("###11###############################\n");

    for (int i = 0; i < BUFFER_COUNT-1; i++) {
        window->cancelBuffer(window.get(), buffers[i], -1);
    }
#endif

    LOGD("###22###############################\n");

    ANativeWindowBuffer* buffer;
    // Fill BUFFER_COUNT-1 buffers
    // for (int i = 0; i < BUFFER_COUNT-1; i++) {
    //     native_window_dequeue_buffer_and_wait(window.get(), &buffer);
    //     window->queueBuffer(window.get(), buffer, -1);
    // }

    uint32_t val = 0;
    ANativeWindowBuffer* pnwb;
    GraphicBuffer *gpb;

    FILE *fp = fopen("/data/local/tmp/1080p_nv12.yuv", "rb");
    if (fp == NULL)
    {
        LOGE("fopen failed!\n");
    }

    while (!g_quit)
    {

        err = native_window_dequeue_buffer_and_wait(window.get(), &pnwb);
        if (err != NO_ERROR)
        {
            LOGE("native_window_dequeue_buffer_and_wait failed, err=%d\n", err);
        }
        else
        {
            LOGD("111 native_window_dequeue_buffer_and_wait ok.\n");

            gpb = static_cast<GraphicBuffer *>(pnwb);

            uint32_t* dataIn;
            gpb->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&dataIn));
            //*dataIn = val;

            int32_t read_size = fread(dataIn, 1, frame_size, fp);
            if (read_size != frame_size)
            {
                rewind(fp);
                read_size = fread(dataIn, 1, frame_size, fp);
                if (read_size != frame_size)
                {
                    LOGE("read error!\n");
                }
            }

            gpb->unlock();

            LOGD("111 native_window_dequeue_buffer_and_wait ok. val=%d \n", val);

            window->queueBuffer(window.get(), pnwb, -1);

            val++;
        }

        //sleep
        //usleep(400000);
        usleep(100000);
    }

    if (fp)
        fclose(fp);

    // Disconnect the surface
    surface->disconnect(NATIVE_WINDOW_API_CPU);

    LOGI("client exited.\n");

    //return 0;
}

#endif